# Учебный проект: Пошаговый слайдер, часть 2

В предыдущей задаче мы познакомились с компонентом интерфейся "Слайдер", и выполнили первую часть по созданию слайдера для проекта. Для решения этой задачи вам понадобятся ваши наработки. 

Для начала скопируйте свое решение предыдущей задачи в `index.js` файл. Изменение значение по клику мы возьмем за основу и дополним новыми функциями. 

Модифируйте класс `StepSlider`, описывающий компонент "Пошаговый слайдер" (для простоты будем называть его просто слайдер). 

Реализуйте изменение значения слайдера с помощью "перетаскивания" ползунка или Drag-and-Drop.

При этом значение слайдера должно меняться динамически по мере перемещения ползунка по слайдеру. 

## Изменение значения с помощью Drag-and-Drop.

За основу работы этого механизма взят алгоритм из статьи [Drag'n'Drop с событиями мыши](https://learn.javascript.ru/mouse-drag-and-drop). Мы настоятельно рекомендуем разобраться в принципе работы примера из статьи перед тем как приступать у решению. 

**Принцип работы Drag-and-Drop:**

1. Пользователь наводит курсор мыши на ползунок слайдера и кликает по нему.
2. Зажав кнопку мыши он перемещает ползунок влево или вправо. Ползунок перемещается по слайдеру, следуя за курсором.
3. Пользователь отпускает кнопку мыши в любом месте слайдера.
4. Ползунок перемещается на ближайший шаг слайдера.

Небольшое отличие этой задачи от описанной в статье в том, что мы будем использовать `Pointer Events` вместо событий мыши. `Pointer Events` - это более универсальные и современные аналоги, которые срабатывают не только для мыши, но и других устройств ввода, например, стилуса на сенсорных экранах. В контексте алгоритма Drag-and-Drop из статьи, мы просто заменим события мыши на аналогичные:

- событию `mousedown` соответствует событие `pointerdown`;
- событию `mousemove` соответствует событие `pointermove`;
- событию `mouseup` соответствует событие `pointerup`;

В остальном для нашей задачи они работают точно также как события мыши.

Напоминаем, что для корректной работы нужно "выключить" встроенный браузерный `Drag-and-Drop` для элемента с классом `slider__thumb`:

```js
// this.elem - ссылка на корневой элемент слайдера
let thumb = this.elem.querySelector('.slider__thumb');

thumb.ondragstart = () => false;
```

Также для устройств с сенсорными экранами, нужно предотвращать действия браузера по умолчанию для событий `pointerdown` и `pointermove`.

### Требования к реализации

1. Элемент для перетаскивания (ползунок) имеет класс - `slider__thumb`.
2. После начала перетаскивания ползунка, корневому элементу слайдера нужно добавить класс `slider_dragging`. После окончания перетаскивания этот класс должен быть удален.
3. Технически перемещать ползунок нужно точно также, как в предыдущей задаче:
```js
// this.elem - ссылка на корневой элемент слайдера
let thumb = this.elem.querySelector('.slider__thumb');
let progress = this.elem.querySelector('.slider__progress');

let leftPercents = 55; // Значение в процентах от 0 до 100

thumb.style.left = `${leftPercents}%`;
progress.style.width = `${leftPercents}%`;
```
4. Ползунок нужно перемещать, даже если во время перетаскивания пользователь увел курсор со слайдера. Подумайте, куда именно нужно повесить обработчик для отслеживания перемещений.
5. По мере перемещения ползунка по слайдеру, нам нужно выбирать конкретные значение шага из доступного диапазона (`0, 1, 2, 3, 4`). Если ползунок находится между какими-то двумя значениями (шагами), то выбрать нужно тот шаг, к которому курсор мыши находится ближе. 

Выбранное значение необходимо отображать динамически во время перемещения:
- Отобразить выбранное значение в верстке, записав его внутрь элемента с классом `slider__value`.
- Визуально выделить шаг на слайдере, добавив класс `slider__step-active` элементу `span` внутри элемента с классом `slider__steps`. Например, если значение - 3, то выделить нужно 4-ый по счету `span`, т.к. у нас счет начинается с 0.

### Генерация пользовательского события

После того как пользователь отпускает кнопку мыши, и выбрано новое значение слайдера, нам нужно сгенерировать событие `slider-change` с новым значением на корневом элементе слайдера, так же как и в предыдущей задаче: 

```js
new CustomEvent('slider-change', { // имя события должно быть именно 'slider-change'
  detail: this.value, // значение 0, 1, 2, 3, 4
  bubbles: true // событие всплывает - это понадобится в дальнейшем
})
```

Не забудьте про это :)

## Подсказка

Если вы хотите решить задачу сами, не читайте дальше ;)

### Как перемещать ползунок и получать значение?

Код приведенный ниже следует писать внутри обработчика события `pointermove`.

Для начала определим расстояние от начала элемента слайдера до места, на котором находился курсор в момент клика. Мы будем использовать координаты относительно окна. Возьмем координату по горизонтали (из свойства `clientX` объекта события) и вычтем из нее координату крайней левой точки слайдера, которую получим с помощью метода `getBoundingClientRect()`:

```js
let left = event.clientX - this.elem.getBoundingClientRect().left; 
// event - объект события "pointermove"
// this.elem - ссылка на корневой элемент слайдера
```

Для получения значения в процентах относительно всего слайдера, разделим предыдущее значение на ширину слайдера и умножим на 100. При этом нужно не забыть, что значение в процентах должно быть в пределах от `0 до 100`: 

```js
let left = event.clientX - this.elem.getBoundingClientRect().left; 
let leftRelative = left / this.elem.offsetWidth;

if (leftRelative < 0) {
  leftRelative = 0;
}

if (leftRelative > 1) {
  leftRelative = 0;
}

let leftPercents = leftRelative * 100;
```

Полученное значение в процентах используем для перемещения ползунка и "закрашивания" области до него:

```js
// this.elem - ссылка на корневой элемент слайдера
let thumb = this.elem.querySelector('.slider__thumb');
let progress = this.elem.querySelector('.slider__progress');

thumb.style.left = `${leftPercents}%`;
progress.style.width = `${leftPercents}%`;
```

Итак, мы переместили ползунок, в тоже время нам нужно получить конкретное значение слайдера (`0, 1, 2, 3, 4`). Для этого возьмем промежуточное относительное значение из вычислений выше (переменная `leftRelative`) и умножим его на количество сегментов: 

```js
// steps - количество шагов слайдера, для нашего примера - 5
let segments = steps - 1;
let approximateValue = leftRelative * segments;
```

В итоге вероятнее всего вы получите некое дробное значение, например: `1.2345`, `3.442` или подобное. При этом оно не будет меньше `0` и не больше максимально возможного значения слайдера, для нашего примера - это `4`. Чтобы получить конкретно значение, которое нужно задать слайдеру, округлим дробное значение по правилам математики:

```js
let value = Math.round(approximateValue);
```

Его мы и будем использовать для отображения в слайдере, как это требуется выше.
